#ifndef TB_REFLECTION_SIMPLE_JSON_READER_H__
#define TB_REFLECTION_SIMPLE_JSON_READER_H__

#include <rapidjson/document.h>

#include "tbcore/predefined.hpp"

#include "../archive/deserializer.hpp"
#include "../stream/json_input_stream.hpp"
#include "../../base/tostring.hpp"

#include "json_parser.hpp"

TB_NAMESPACE_BEGIN

template <typename ReadBuffer>
class JsonReader {
 public:
  BOOST_STATIC_ASSERT(is_read_buffer<ReadBuffer>::value);

  typedef typename JsonParser<ReadBuffer>::JsonInputStreamType JsonInputStreamType;

  typedef typename JsonParser<ReadBuffer>::JsonEncodeType JsonEncodeType;

  typedef typename JsonParser<ReadBuffer>::JsonValueType JsonValueType;

  typedef typename JsonParser<ReadBuffer>::JsonDocumentType JsonDocumentType;

  typedef std::unique_ptr<JsonDocumentType> JsonDocumentTypePtr;

  JsonReader(ReadBuffer& rb) 
    : buffer_(rb), 
      value_(nullptr), 
      doc_(new JsonDocumentType()) {};

  bool Parse() {
    if (!value_ || value_ != doc_.get()) {
      JsonParser<ReadBuffer>::ParseBuffer(buffer_, *doc_);

      if (!doc_->HasParseError()) {
        value_ = doc_.get();
        return true;
      } else {
        LERROR() <<  "parsing error, code: " << doc_->GetParseError() << " offset: " << doc_->GetErrorOffset();
      }
    }
    return false;
  }

  JsonValueType* GetValue() const {
    return value_;
  }

  void ReadValue(Variant& obj) {
    return obj.Assign(ReadObject(*value_));
  }

 private:
  Variant ReadStruct(const JsonValueType& val) {
    TB_ASSERT(val.IsObject());

    const JsonValueType& typeNameValue = val[_T("Type")];
    if (typeNameValue.IsNull()) {
      LKERNEL() << "json parsing error: \"Type\" lost";
      return Variant();
    }

		if (const reflection::TypeInfo* info = reflection::GetTypeInfo(typeNameValue.GetString())) {
			Variant v = reflection::ConstructType(info, true);

      if (v.IsNull() || v.TypeId() != info->id) {
        return Variant();
      }

      const JsonValueType& valueObj = val[_T("Value")];
      if (valueObj.IsNull()) {
        LKERNEL() << "json parsing error: \"Value\" lost";
        return Variant();
      }

      if (valueObj.IsArray() && valueObj.Size() % 2 == 0) {
				const reflection::ConstPropertiesType& properties = reflection::GetProperties(info->id);

        for (RAPIDJSON_NAMESPACE::SizeType i = 0; i + 1 < valueObj.Size(); i += 2) {
          const JsonValueType& propNameVal = valueObj[i];
          const JsonValueType& propValueVal = valueObj[i + 1];
          if (propNameVal.IsString()) {
						const TCHAR* propName = propNameVal.GetString();

            if (strlen_ys(propName) == 0) {
              continue;
            }
              
						reflection::ConstPropertiesType::const_iterator findIter
              = std::find_if(properties.begin(), properties.end(), 
              [&propName](const reflection::Property* prop) -> bool {
								return prop->name == propName;
              }
            );

            if (findIter != properties.end()) {
              Variant pv = ReadObject(propValueVal);

              if (!pv.IsNull()) {
                (*findIter)->Set(v.DataPtr(), pv);
              }
            }
          }
        }
      }
      return v;
    }

    return Variant();
  }

  Variant ReadMap(const JsonValueType& val) {
    const JsonValueType& values = val[_T("Value")];

    if (values.IsNull()) {
      LKERNEL() << "json parsing error: \"Value\" lost";
      return Variant();
    }

    if (!values.IsArray() || values.Size() % 2) {
      LKERNEL() << "json parsing error: value is not \
								 array or array size is not even!";
      return Variant();
    }

    Variant v(0, Variant::kComplexTypeFlag, Variant::kMapDataFlag);
		for (RAPIDJSON_NAMESPACE::SizeType i = 0; i < values.Size(); i += 2) {
			const JsonValueType& keyObj = values[i];
			const JsonValueType& valueObj = values[i + 1];
			v.Insert(ReadObject(keyObj), ReadObject(valueObj));
		}

    return v;
  }

  Variant ReadArray(const JsonValueType& val) {
    const JsonValueType& values = val[_T("Value")];

    if (values.IsNull()) {
      LKERNEL() << "json parsing error: \"Value\" lost";
      return Variant();
    }

    if (!values.IsArray()) {
      LKERNEL() << "json parsing error: value is not array!";
      return Variant();
    }

    Variant v(0, Variant::kComplexTypeFlag, Variant::kArrayDataFlag);
    if (values.IsArray() && values.Size() > 0) {
      for (RAPIDJSON_NAMESPACE::SizeType i = 0; i < values.Size(); ++i) {
        const JsonValueType& valueObj = values[i];
        v.PushBack(ReadObject(valueObj));
      }
    }

    return v;
  }

  Variant ReadSimpleValue(const JsonValueType& val) {
    if (val.IsBool()) {
      return Variant::FromValue(val.GetBool());
    } else if (val.IsInt()) {
      return Variant::FromValue(val.GetInt());
    } else if (val.IsUint()) {
      return Variant::FromValue(val.GetUint());
    } else if (val.IsInt64()) {
      return Variant::FromValue(val.GetInt64());
    } else if (val.IsUint64()) {
      return Variant::FromValue(val.GetUint64());
    } else if (val.IsDouble()) {
      return Variant::FromValue(val.GetDouble());
    } else if (val.IsString()) {
      return Variant::FromValue(String(val.GetString(), val.GetStringLength()));
    }

    return Variant();
  }

  Variant ReadObject(const JsonValueType& val) {
    if (val.IsNumber() || val.IsBool() || val.IsString()) {
      return ReadSimpleValue(val);
    }

    if (val.IsObject()) {
      const JsonValueType& valValue = val[_T("Value")];

      if (valValue.IsNull()) {
        LKERNEL() << "json parsing error: \"Value\" lost";
        return Variant();
      }

      if (valValue.IsObject() || valValue.IsArray()) {
        const JsonValueType& flagValue = val[_T("Flag")];

        if (flagValue.IsNull()) {
          LKERNEL() << "json parsing error: \"Flag\" lost";
          return Variant();
        }

        if (!flagValue.IsNumber()) {
          LKERNEL() << "json parsing error: \"Flag\" must be a number";
          return Variant();
        }

        switch (flagValue.GetInt()) {
					case Variant::kNullDataFlag: {
						return ReadSimpleValue(valValue);
					}
					case Variant::kObjectDataFlag: {
						return ReadStruct(val);
					}
					case Variant::kMapDataFlag: {
						return ReadMap(val);
					}
					case Variant::kArrayDataFlag: {
						return ReadArray(val);
					} 
        }
      }
    }

    return Variant();
  }

 private:
  ReadBuffer &buffer_;
  JsonDocumentTypePtr doc_;
  JsonValueType* value_;
};

TB_NAMESPACE_END

#endif // TB_REFLECTION_SIMPLE_JSON_READER_H__
